package IC.Parser;

%%

%class Lexer
%public
%function next_token
%type Token
%line
%scanerror LexicalError
%state COMMENT
%state SINGLE_LINE_COMMENT
%cup

%eofval{ 
	if (isInComment){
		throw new LexicalError(getMessage("Lexical Error: Unclosed comment began in line " + beginCommentLineNumber));
	}
	else{
		return new Token(sym.EOF, yyline(), "EOF");
	}
		
%eofval}

ALPHA_UPPER = [A-Z]
ALPHA_LOWER = [a-z]
ALPHA = [A-Za-z_]
DIGIT = [0-9]
ALPHA_NUMERIC = {ALPHA}|{DIGIT}
IDENT = {ALPHA_LOWER}({ALPHA_NUMERIC})*
CLASS_IDENT = {ALPHA_UPPER}({ALPHA_NUMERIC})*
NUMBER = [1-9]({DIGIT})*|0+
IGNORED_SYMBOLS = [\n]|[\r]|[\t]|[ ]
ESCAPE_SYMBOLS = "\\\""|"\\\\"|"\\t"|"\\n"
SPECIAL_SYMBOLS = [ !#$%&'()*+,-./:;<=>?@\[\]\^_`{|}~]
QUOTED_STRING = \"({ALPHA_NUMERIC}|{SPECIAL_SYMBOLS}|{ESCAPE_SYMBOLS})*\"
ILLEGAL_NUMBER = 0+({DIGIT})+
ILLEGAL_QUOTED_STRING = \"({ALPHA_NUMERIC}|{SPECIAL_SYMBOLS}|{ESCAPE_SYMBOLS})*
ILLEAGL_IDENT = ({NUMBER}{IDENT})|({NUMBER}{CLASS_IDENT})

OTHER = .|\n

%{
	private boolean isInComment = false;
	private int beginCommentLineNumber;
	int yyline(){
		return yyline + 1;
	}
	
	
	private String getMessage(String message){
		return (yyline() + ": " + message);
	}
%}

%%

<YYINITIAL> "=" { return new Token(sym.ASSIGN,yyline(), "ASSIGN"); }
<YYINITIAL> "boolean" { return new Token(sym.BOOLEAN,yyline(), "BOOLEAN"); }
<YYINITIAL> "break" { return new Token(sym.BREAK,yyline(), "BREAK"); }
<YYINITIAL> "class" { return new Token(sym.CLASS,yyline(), "CLASS"); }
<YYINITIAL> "," { return new Token(sym.COMMA,yyline(), "COMMA"); }
<YYINITIAL> "continue" { return new Token(sym.CONTINUE,yyline(), "CONTINUE"); }
<YYINITIAL> "/" { return new Token(sym.DIVIDE,yyline(),"DIVIDE"); }
<YYINITIAL> "." { return new Token(sym.DOT,yyline(), "DOT"); }
<YYINITIAL> "==" { return new Token(sym.EQUAL,yyline(), "EQUAL"); }
<YYINITIAL> "extends" { return new Token(sym.EXTENDS,yyline(), "EXTENDS"); }
<YYINITIAL> "else" { return new Token(sym.ELSE,yyline(), "ELSE"); }
<YYINITIAL> "false" { return new Token(sym.FALSE,yyline(), "FALSE"); }
<YYINITIAL> ">" { return new Token(sym.GT,yyline(), "GT"); }
<YYINITIAL> ">=" { return new Token(sym.GTE,yyline(), "GTE"); }
<YYINITIAL> "if" { return new Token(sym.IF,yyline(), "IF"); }
<YYINITIAL> "int" { return new Token(sym.INT,yyline(), "INT"); }
<YYINITIAL> "&&" { return new Token(sym.LAND,yyline(), "LAND"); }
<YYINITIAL> "[" { return new Token(sym.LB,yyline(), "LB"); }
<YYINITIAL> "(" { return new Token(sym.LP,yyline(), "LP"); }
<YYINITIAL> "{" { return new Token(sym.LCBR,yyline(), "LCBR"); }
<YYINITIAL> "length" { return new Token(sym.LENGTH,yyline(), "LENGTH"); }
<YYINITIAL> "new" { return new Token(sym.NEW,yyline(), "NEW"); }
<YYINITIAL> "!" { return new Token(sym.LNEG,yyline(), "LNEG"); }
<YYINITIAL> "||" { return new Token(sym.LOR,yyline(), "LOR"); }
<YYINITIAL> "<" { return new Token(sym.LT,yyline(), "LT"); }
<YYINITIAL> "<=" { return new Token(sym.LTE,yyline(), "LTE"); }
<YYINITIAL> "-" { return new Token(sym.MINUS,yyline(), "MINUS"); }
<YYINITIAL> "%" { return new Token(sym.MOD,yyline(), "MOD"); }
<YYINITIAL> "*" { return new Token(sym.MULTIPLY,yyline(), "MULTIPLY"); }
<YYINITIAL> "!=" { return new Token(sym.NEQUAL,yyline(), "NEQUAL"); }
<YYINITIAL> "null" { return new Token(sym.NULL,yyline(), "NULL"); }
<YYINITIAL> "+" { return new Token(sym.PLUS,yyline(), "PLUS"); }
<YYINITIAL> "]" { return new Token(sym.RB,yyline(), "RB"); }
<YYINITIAL> "}" { return new Token(sym.RCBR,yyline(), "RCBR"); }
<YYINITIAL> "return" { return new Token(sym.RETURN,yyline(), "RETURN"); }
<YYINITIAL> ")" { return new Token(sym.RP,yyline(), "RP"); }
<YYINITIAL> ";" { return new Token(sym.SEMI,yyline(), "SEMI"); }
<YYINITIAL> "static" { return new Token(sym.STATIC,yyline(), "STATIC"); }
<YYINITIAL> "string" { return new Token(sym.STRING,yyline(), "STRING"); }
<YYINITIAL> "this" { return new Token(sym.THIS,yyline(), "THIS"); }
<YYINITIAL> "true" { return new Token(sym.TRUE,yyline(), "TRUE"); }
<YYINITIAL> "void" { return new Token(sym.VOID,yyline(), "VOID"); }
<YYINITIAL> "while" { return new Token(sym.WHILE,yyline(), "WHILE"); }
<YYINITIAL>  {IGNORED_SYMBOLS} {}

<YYINITIAL> {NUMBER} {
		String number = yytext();
		try {
			new Integer(number);
		} catch (NumberFormatException e){
			throw new LexicalError(getMessage("Lexical Error: the number is not a valid integer.")); 
		}
		return new Token(sym.INTEGER, number, yyline(), "INTEGER"); }

<YYINITIAL> {CLASS_IDENT} { return new Token(sym.CLASS_ID, yytext(), yyline(), "CLASS_ID"); }
<YYINITIAL> {IDENT} { return new Token(sym.ID, yytext(), yyline(), "ID"); }
<YYINITIAL> {QUOTED_STRING} { 
	String quote = (yytext().substring(1,yytext().length()-1)).replace("\\n","\n").replace("\\\\","\\").replace("\\t","\t").replace("\\\"","\"");
	return new Token(sym.QUOTE, quote, yyline(), "QUOTE"); 
}

<YYINITIAL> "//" { yybegin(SINGLE_LINE_COMMENT); }
<SINGLE_LINE_COMMENT> [^\n] {}
<SINGLE_LINE_COMMENT> [\n] { yybegin(YYINITIAL); }

<YYINITIAL> "/*" { 
					isInComment = true;
					beginCommentLineNumber = yyline();
					yybegin(COMMENT); }
<COMMENT> . {}
<COMMENT> [\n] {}
<COMMENT> "*/" {
				isInComment = false; 
				yybegin(YYINITIAL); }
				
<YYINITIAL> {ILLEGAL_QUOTED_STRING} { throw new LexicalError(getMessage("Lexical Error: Unclosed string")); }
<YYINITIAL> {ILLEAGL_IDENT} { throw new LexicalError(getMessage("Lexical Error: illegal identifier.")); }
<YYINITIAL> {ILLEGAL_NUMBER} { throw new LexicalError(getMessage("Lexical Error: Non-Zero numbers should not have leading zeros.")); }
<YYINITIAL> {OTHER} { throw new LexicalError(getMessage("Lexical Error: Illegal character or sequence.")); }